Add the /errors section and the /e/<id> error-message redirector - #521
Open
g-despot wants to merge 7 commits into
Open
Add the /errors section and the /e/<id> error-message redirector#521g-despot wants to merge 7 commits into
g-despot wants to merge 7 commits into
Conversation
There was a problem hiding this comment.
Orca Security Scan Summary
| Status | Check | Issues by priority | |
|---|---|---|---|
| Infrastructure as Code | View in Orca | ||
| SAST | View in Orca | ||
| Secrets | View in Orca | ||
| Vulnerabilities | View in Orca |
4 tasks
g-despot
marked this pull request as draft
August 20, 2026 11:23
Contributor
Author
|
Converting to draft. The destinations are being redesigned: instead of pointing at existing docs pages, |
Error and warning messages from Weaviate core and the clients now carry a stable short link, https://docs.weaviate.io/e/<id>, instead of a deep docs URL. The id is the contract; the destination is not. A message string ships inside a released binary and can never be changed for users already running it, so the id-to-page mapping lives here: a docs restructure becomes a one-line redirect edit instead of six client releases, and it fixes readers on old client versions retroactively. Ids are <origin>-<category><nnn>, lowercase. The origin prefix is load-bearing: the categories (auth, dep, con, bat, grpc, rbac) are universal, so core and every client would each want a "deprecation" bucket and a flat namespace would collide by default. It is not hypothetical -- the Python client already overloads con004 and dep026 against two different messages each. - 29 destination rules: 28 py-* and 1 core-mem001. - 32 frozen aliases mapping the bare ids that ship in released weaviate-client wheels (Dep004: ...) onto their py-* form. These can never be renamed or removed and the list is closed. - 5 of those aliases (auth002/003/004, dep028, grpc002) point at a py-* id with no destination yet, because the docs do not cover what those messages tell the user to do. They 404 today, exactly as the bare ids already do. Deliberately not pointed at approximately-related pages. Every destination was verified against this commit's sources: URL resolved to a file, anchors regenerated with the real @docusaurus/utils slugger (per-file dedup state, {#explicit} overrides honored) and checked for uniqueness. The one non-heading anchor, env-vars#GRPC_MAX_MESSAGE_SIZE, was verified against src/components/APITable/index.jsx, which emits <tr id={firstCellText}>. netlify.toml only. No client, core or docs content page was touched. Refs: board 892e2d98 (parent), ac9a24a8 (client renumber + lowercased URLs), 7ece4ca8 (content gaps), 4090df96 (aggregate.md teaches a deprecated arg).
…he prefix rationale
Two review findings on fa6efe9b. Rule count is unchanged at 61 (29 destinations
+ 32 aliases); no rule was added or removed.
1. py-dep010 (and its alias dep010) pointed at
client-libraries/python#library-imports. The anchor exists, is unique, and the
page returns 200 -- and the link was still broken. That heading is nested in a
collapsed <details> ("Migration guides - beta releases"). Docusaurus renders
<details> React-controlled via theme-common Collapsible and does not act on
the URL hash, so the browser's native auto-expand never fires and the reader
lands on the page seeing nothing. It is also a historical v4.4b7 beta
migration note, which is a poor destination for a warning still emitted today,
so forcing the <details> open would not have been a fix either.
Repointed to client-libraries/python/notes-best-practices#helper-classes:
current guidance, and it teaches the exact fix the message asks for
("from weaviate.classes.config import Property" -- import from the submodule,
not the weaviate root). Its prose and both code samples render outside the
<details> on that section; only the submodule table is collapsed.
2. The header comment and the alias-table note both justified the origin prefix
with the Python self-collision (con004/dep026). That is the wrong evidence.
An origin prefix prevents CROSS-origin collision -- the categories are
semantic, so core would reach for Dep0xx and Python already owns 19 of them --
and that reason is sufficient alone. A prefix cannot fix a WITHIN-origin
collision, since py-con004 is just as ambiguous as con004. The self-collision
argues for a different rule: ids must be assigned from a registry, not
harvested from prefixes a client already happens to use. Both points kept,
the second no longer presented as proof of the first.
Anchor verification now has a third mandatory axis. Existence and uniqueness are
not sufficient; an anchor must also RENDER VISIBLE. All 29 destinations were
re-checked statically for collapsed-<details> ancestry (0 hidden) and the
affected pages confirmed in headless Chrome against the post-JS DOM, with
#library-imports as a positive control to prove the check detects the bug.
Refs: board 4bb13003 (the <details> anchor gotcha), 892e2d98 (parent),
ac9a24a8, 7ece4ca8, 4090df96.
…ve a page
A reader who follows an /e/<id> link arrives with one question, and the docs
page they landed on was written for someone browsing. /errors is written for the
arrival instead: one entry per message, carrying the message text verbatim, the
cause, and the fix, so the entry is a complete answer rather than a signpost.
Mechanics, in order of how much was already here:
- Out of the navigation: the pages are simply absent from sidebars.js, the same
way /deploy/faqs/* and /weaviate/concepts/* already are. They stay in the
sitemap and carry no noindex, because people search error text verbatim and
that is a primary way they will arrive. Out of nav is about the sidebar, not
about hiding.
- No table of contents: hide_table_of_contents, already used by ten index pages.
- Right-hand panel: src/theme/DocItem/Layout was ALREADY ejected, so this is a
~10 line change to it rather than a new swizzle. It is keyed on a `side_panel`
frontmatter field that only these pages set, so a page without it renders
through the unchanged path.
/improve-your-cluster ships with the panel that links to it, so the link is live
on day one.
Anchors are descriptive and explicit ({#...}), never the message id: several ids
share one anchor when they share one fix, which is the whole reason not to name
anchors after ids. py-dep017/023/024/025 share one; py-dep011/012/013 share one.
netlify.toml: only the `to` of the ten ids that now have an entry changed. The
32 frozen aliases, the lowercase rule and the rule ordering are untouched. One
stale sentence in the block comment is corrected: Docusaurus does check anchors
and warn, it just never reads this file.
_build_scripts/validate-links-pr.js seeds /errors and /improve-your-cluster.
linkinator only recurses under a seed's own path, so an out-of-nav section that
nothing links down into would never have been crawled at all.
…dead ends RETRACTION FIRST. The message on abdab548 claims these pages are out of nav "the same way /deploy/faqs/* and /weaviate/concepts/* already are". That is wrong: both of those have autogenerated sidebars in sidebars.js and render one. The pattern is precedented, just not by them. /support, /deploy/installation-guides, /contributor-guide/getting-started and /query-agent/guides/research_mode are among 37 built pages with no doc sidebar, counted from the build output. The same message says hide_table_of_contents is "already used by ten index pages"; the count is right, but weaviate/recipes.mdx is not an index page, so read it as ten existing pages. THREE THINGS WERE FACTUALLY WRONG. - The bare-id claim was backwards. Bare ids like Dep024 are what ships TODAY in released packages; the prefixed form is the new one. The page told a reader looking at Dep024 in their terminal that they were on an old release. - "For multimodal embeddings, the Google integration is not split by service" confused the module with the configuration. There is one multimodal module, but it still selects a service: multi2vec_google needs a project and a region, multi2vec_google_gemini needs neither. As written, a Gemini user migrating off multi2vec-palm was asked for a project id they do not have. Both forms now shown, both executed. - Adoption was overstated. Only the Python client prints ids. Core does not print one as a prefix at all: it is gaining a docs_url log field that carries the id inside the URL, and that is not released yet. Said accurately now. SELF-CONTAINEDNESS, which is the whole promise of the section. - The second message shipping under py-dep026 (the `encoding` argument) was quoted nowhere, so a reader searching their literal string would have found nothing on the page their own redirect chose. Quoted verbatim in the multi-vector entry, and py-dep026 added to that entry's id line. - The credential-header paragraph said the header was renamed without naming a single header. Now a table: X-Goog-Vertex-Api-Key and X-Goog-Studio-Api-Key are current, four older spellings are deprecated. - The caution box named no error string. Now a table of all three, each reproduced live: KeyError 'vectorConfig' (legacy collection, current form), KeyError 'vectorIndexConfig' (named-vector collection, deprecated form: the deprecated argument is not merely deprecated there, it is broken), and WeaviateInvalidInputError for a name that does not exist. The log excerpt on cluster-resources presented three lines from three different code paths as one block, and two of the three carry the error in a separate `error` field rather than in the message. Now a table saying which is which. NAVIGATION. Out of every sidebar means no breadcrumbs, no paginator, no TOC, and a desktop-only side panel that points forward rather than back. Every group page now links to /errors in its intro, above the fold on mobile as well. netlify.toml gains an /e/* catch-all, LAST in the file so first-match keeps every specific rule winning. An unknown id used to 404; it now reaches the index that explains what an id is. It also upgrades the five aliases whose canonical id is deliberately unwritten (auth002, auth003, auth004, dep028, grpc002) from a 404 to a page. The checker now asserts the catch-all is last and that no other splat exists in the block. Also: the PaLM reconciliation moved into a note directly under the quoted messages, four agentless passives made second person, and model-integrations now says up front that these are Python client warnings and cross-links the vector configuration entry, since its "Current" snippets silently carry that change too. Verified: yarn build and yarn build-dev exit 0, no new broken links or anchors; 405/405 routes 200; 24 /e/ ids resolved through the table land on a visible anchor or the index; all 17 snippet variants executed against Weaviate 1.38.4, every deprecated form warns and every current form is silent.
…abricated log line
THE TENANT-ACTIVATION LOG LINE: the review finding was inverted, and the trap
that produced it is worth recording, because it has now caught three readers.
v1.39.0 tag .WithError(err).Errorf("loading shard %q failed", name)
v1.38.10 tag .WithError(err).Errorf("loading shard %q failed", name)
origin/main .WithError(err).Errorf("loading shard %q failed", name)
stable/v1.39 branch .Errorf("loading shard %q failed: %v", name, err)
The `: %v` form exists only on stable/v1.39. It arrived in c0a1f9bfa9 (2026-08-13),
`git tag --contains` on that commit is EMPTY, the branch is 692 commits past the
v1.39.0 tag, and the commit is not on main either. So it ships in no release
today. The bare checkout at ~/dev/weaviate sits on stable/v1.39, which is what
makes it read like main.
Second trap, same area: in zsh `git show $tag:path` silently prints NOTHING,
because `:path` is eaten as a history modifier. It exits 0, so it reads as "the
string is absent" rather than "the command did not run". Brace the ref.
Rather than pin the row to a version boundary that may move again, the row now
says the cause is either appended to that message or in a sibling `error` field
depending on the version, and tells the reader to search for the phrase rather
than the line. That is true on every version, released or not.
Also dropped: a JSON log entry I had synthesized to illustrate the field layout.
It was never captured from a running node, and on a page whose whole contract is
that the reader matches what is literally on their screen, an invented example is
the one thing that must never appear. The format and the fields are described
instead.
CATCH-ALL IS NOW 302, and it is the only rule in the block that is. The specific
rules stay 301 because an id means one thing forever and caching that is right.
The catch-all is the opposite: it fires for ids whose entry is not written yet,
so a 301 sitting in a browser cache would keep sending someone to the index long
after their id got a real entry, and no deploy could reach them.
THE INVARIANT IS NOW ACTUALLY ENFORCED. I had claimed the ordering was
machine-guarded when it lived in an uncommitted local script, which protects
nobody after this merges. _build_scripts/validate-redirects.js is committed,
wired into `yarn validate-redirects` and into the PR workflow ahead of the build,
and needs no build or network. It checks: the catch-all is last and 302, every
specific rule is 301, sources are lowercase and unique, and every /errors
destination resolves to a real page and a real {#anchor}. Each of those six
checks was confirmed to fail on a deliberately mutated copy before landing.
The Dep026 note pointed at the wrong name. The warning text says
`Configure.VectorIndex.MultiVectors`, plural, which does not exist; the working
accessor is `MultiVector`, singular. The plural is easy to reach for because
`Configure.MultiVectors` does exist at the top level, so the note now says which
is which instead of just "there is a typo".
Server floor added to the Google header table: the current headers need v1.27.7,
v1.26.12 or v1.25.27. A cluster still configured for PaLM is disproportionately
likely to predate them and would answer with an authentication error.
Snippet harness now prints the count it executed. I had reported 17 variants; it
is 16, and the number is no longer transcribed by hand.
…y under-parsing The checker had the defect it existed to prevent. It matched one regex with a fixed key order, fixed double quotes and three literal spaces in `to =`, so a rule appended after the catch-all became INVISIBLE to it the moment the formatting drifted. Confirmed against the previous version, which exits 0 and prints "1 catch-all (302, last)" while a dead rule sits after the catch-all: one space in `to = "` OLD exit 0 NEW exit 1 keys reordered OLD exit 0 NEW exit 1 single-quoted strings OLD exit 0 NEW exit 1 `force = true` interleaved OLD exit 0 NEW exit 1 a source the parser misses OLD exit 0 NEW exit 1 (`force = true` appended after `status` was still caught by the old pattern, since the first three keys still matched in order; interleaved is the case that slips. Four of five, not four of four.) Nothing is missed in the file as it stands today. It would have bitten on the next hand-written edit, which is precisely when spacing drifts and precisely the edit this check exists for. TWO CHANGES, and the second is the one that matters. Redirect tables are now parsed as tables: split on [[redirects]], read whatever keys are present, any order, single or double quotes, extra keys ignored. That closes the four known holes. More importantly the parse is now asserted COMPLETE. The /e/ sources are counted independently with the loosest pattern that can recognise one, and if that count differs from the number of rules parsed the script exits 1 with PARSE GAP before running a single check. Every other check reasons over the parsed set, so an under-parse would let all of them pass over a set they never saw: success reported over unexamined input, which is the same failure shape as a link checker that validates zero files. Rule 1 closes the holes we found; rule 0 closes the ones we did not. Comments are stripped before both parsing and counting, so prose in the block that quotes these rules can neither be read as one nor counted as one. Also added: a /e/ rule with no `to` or no `status` now fails rather than being carried as a half-parsed object. All twelve mutations were confirmed to exit 1, and netlify.toml was diffed byte-identical afterwards. NIT: the docs_url sample was written in logrus text format on a page that tells the reader JSON is the default. It is now shown as a JSON key, with the text form named as the LOG_FORMAT=text alternative. The default was re-verified at v1.39.0 and v1.38.10: configure_api.go sets the JSON formatter unless LOG_FORMAT=text.
g-despot
force-pushed
the
docs/error-message-redirector
branch
from
August 20, 2026 15:54
dbb0413 to
1b02ca6
Compare
g-despot
marked this pull request as ready for review
August 20, 2026 16:00
… cluster id A /e/<id> redirect destination that carries its own query string makes Netlify drop the query the request arrived with, rather than merging the two. So the message id is set before the redirect rules run instead of being written into the 29 destinations, and every destination stays query-free. ClusterIdNotice renders on /improve-your-cluster and the four /errors pages when a link carries a cluster id. Absent is the normal case, not an error: a cluster has no identity until its raft leader commits one, and may never have one at all. Absent, empty and malformed all render nothing. The check is a format check with no lookup, so the page cannot be used to tell an id that exists from one that does not, and the pattern is version agnostic because core mints a v7 and falls back to v4. The value is read after mount so the client's first render matches the query-free server HTML. No canonical tag is added; Docusaurus already emits a route-derived one with no query string.
g-despot
force-pushed
the
docs/error-message-redirector
branch
from
August 20, 2026 20:12
5e693bc to
11fad73
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Error messages and log lines are gaining a stable id that resolves through
https://docs.weaviate.io/e/<id>to a page explaining the error. This adds the redirect table and the/errorssection it points into.The section. Pages group errors by kind of problem, out of the site navigation but still indexable, since people google error text verbatim. Each entry gives the message verbatim, the cause, and the fix, so a reader who arrived from an error can act without following another link. Several ids share one subheading where they share one fix. No TOC; a side panel links to the new
/improve-your-clusterpage.The redirects. 62 rules: 29 destinations, 32 frozen aliases for the bare ids already shipping in released
weaviate-clientwheels (those can never be renamed), and a trailing catch-all so an unrecognised id lands on the index rather than 404ing someone already having a bad day. The catch-all is a 302, not a 301, because its destination changes as each id gets a real page and a cached 301 would pin readers to the index permanently.Ten ids point into
/errorstoday. The rest keep their current destinations until their page exists; five resolve to the index by design, tracked with the content gaps._build_scripts/validate-redirects.jsguards the invariants in CI, ahead of the build: catch-all last and 302, specific rules 301, sources lowercase and unique, and every/errorsdestination resolving to a real page and a real explicit anchor. It refuses to run its own checks if it cannot account for every declared rule, because a checker that silently under-parses reports success over input it never saw.Merge before weaviate/weaviate#12738, which emits
/e/core-mem001and 404s until this is live.Anchors were verified by rendering, not just existence: one destination passed existence, uniqueness and HTTP 200 while sitting inside a collapsed
<details>, invisible to the reader. Worth knowing generally, since it applies well beyond this PR: the build warns on broken anchors but does not fail, only sees links rendered through Docusaurus<Link>, and never readsnetlify.toml.